﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Interface;

namespace RayDen.SmallPT
{
    public class EngineConfig : IRenderConfig
    {
        private bool started;
        private int width, height;
        private ImagePlane imagePlane;
        private FrameDescription frame;
        private IRenderer render;
        private Task renderTask;
        public string FrameName { get { return frame.FrameName; } }

        public EngineConfig()
        {

        }

        #region IRenderConfig Members

        public void RunCommand(int index)
        {}

        public string SavePath
        {
            get { return frame.Get("ImageSaveDirectory", @"F:\Dev\Images\MovieFactory\"); }
        }

        public int Width
        {
            get { return width; }
        }

        public int Height
        {
            get { return height; }
        }

        public void SaveImage(string fn)
        {
            ImageFactory.FreeImageSaveExrBitmap(fn, this.imagePlane.Width, imagePlane.Height, imagePlane.Data);
            ImageFactory.SaveSRgbTexture(fn, this.imagePlane.Width, imagePlane.Height, imagePlane.Data);

        }

        public void SaveRawImage(string fn)
        {
            ImageFactory.FreeImageSaveExrBitmap(fn, this.imagePlane.Width, imagePlane.Height, imagePlane.Data);

        }
        public void OpenFrame(FrameDescription frame)
        {
            this.frame = frame;
            width = frame.Get<int>("ImageWidth");
            height = frame.Get<int>("ImageHeight");

            render = this.SetupRender();

            imagePlane = new ImagePlane(width, height);
            render.Setup(frame);
        }

        private IRenderer SetupRender()
        {
            var result =
                //new RayTracer();
                //
                //new MLRayTracer() 
                //new AmbientOcclussionRenderer()
                //new PathTracerRenderer() 
                new MCPathIntegrator()
                //new MonteCarloRayTracer()
                //new PhotonMapEngine() 
                //new ProgressivePhotonMappingEngine()
                    { CpuThreadsUsed = 8 };
            result.SamplesPerPixel = 128;
            return result;
        }

        public void Start()
        {
            started = true;
            Render(render);
        }

        public void Interrupt()
        {
            started = false;
        }

        public void ConsumeJob(IRenderJob job, Action<IRenderJobResult> resultCallback)
        {
            throw new NotImplementedException();
        }

        public string GetStats()
        {
            return render.GetCurrentStateInfo();
        }

        public IImagePlaneComponent GetImagePlane()
        {
            return this.imagePlane;
        }

        public void Save(string fn)
        {
            //(this.imagePlane).SavePNG(fn, GetStats().Split(';').Select(item => item.Trim()).Union(new[] { string.Format("Engine - {0}", this.engineType) }).ToArray());
        }
        #endregion

        private void PreviewSplatFilm(IFilmFrame frame, int curY = -1, int pixelsInPreview = 10000)
        {
            var bmpWidth = frame.Width;
            var bmpHeight = (curY > 0 ? curY : frame.Height);
            var rnd = new FastRandom();
            RgbSpectrum.Gamma = true;
            for (int n = 0; n < pixelsInPreview; n++)
            //for (int i = 0; i < bmpHeight; i++)
            {
                //for (int j = 0; j < bmpWidth; j++)
                {
                    var i = rnd.Next(0, bmpHeight);
                    var j = rnd.Next(0, bmpWidth);
                    var pixOffset = frame.Width * ((bmpHeight - i - 1)) + j;
                    var color = ((frame.Data[pixOffset] / frame.Weight[pixOffset])).Transform();
                    this.imagePlane.Data[pixOffset] = color;
                }
            }
        }

        private void SplatFilm(IFilmFrame frame, int curY = -1)
        {
            var bmpWidth = frame.Width;
            var bmpHeight = (curY > 0 ? curY : frame.Height);
            for (int i = 0; i < bmpHeight; i++)
            {
                for (int j = 0; j < bmpWidth; j++)
                {
                    var pixOffset = frame.Width * ((bmpHeight - i - 1)) + j;
                    var color = ((frame.Data[pixOffset] / frame.Weight[pixOffset])).Transform();
                    this.imagePlane.Data[pixOffset] = color;
                }
            }
        }
        private void Render(IRenderer render)
        {
            Console.WriteLine();
            renderTask = new Task(() => render.StartRendering((film, i) =>
            {
                if (i % 10 == 0)
                    Console.Write("*");
                this.PreviewSplatFilm(film);
            }, (film) => this.SplatFilm(film)));
            renderTask.Start();
        }

        #region IRenderConfig Members


        public void Dispose()
        {
            if (renderTask != null)
            {
                if (renderTask.Status == TaskStatus.Running)
                {
                    renderTask.Wait(new CancellationToken(true));
                }
                renderTask.Dispose();
            }
        }

        #endregion
    }
}
